package org.datacoder.geocoding.datastructures.intervals

import com.vividsolutions.jts.geom.*
import org.datacoder.geocoding.*
import org.datacoder.geocoding.datastructures.geoshapes.*
import org.datacoder.geocoding.jurisdiction.*

class BucketsSubStructure {

    private static final int AVERAGE_NUM_JURISDICTIONS = 20
    private static final GeoInterval[] ZERO_LENGTH_ARRAY = new GeoInterval[0]
    private GeoInterval[][][] buckets
    private double minDoubleValue, maxDoubleValue, doubleStep
    private double minSingleValue, maxSingleValue, singleStep
    private int dimensions
    int type

    public BucketsSubStructure(Collection<Jurisdiction> jurisdictions, Collection<Jurisdiction> envelopeCollection, int type){
        this.type = type
        dimensions = (Math.sqrt((1.0*jurisdictions.size())/AVERAGE_NUM_JURISDICTIONS))

        // Determine dimensions of grid
        Polygon[] polygons = new Polygon[envelopeCollection.size()]
        Iterator envelopeCollectionIterator = envelopeCollection.iterator()
        for(int i = 0; i < polygons.length; i++){
            Jurisdiction juris = envelopeCollectionIterator.next()
            polygons[i] = (Polygon)(juris.toGeoRectangle().toGeometry())
        }
        GeometryCollection envelopeMaker = new GeometryCollection(polygons, GeocodingUtils.geometryFactory)
        Envelope envelope = envelopeMaker.getEnvelopeInternal()
        double lon1 = envelope.getMinX()
        double lon2 = envelope.getMaxX() + 0.01
        double lat1 = envelope.getMinY()
        double lat2 = envelope.getMaxY() + 0.01
        double lonStep = (lon2 - lon1)/dimensions
        double latStep = (lat2 - lat1)/dimensions
        if(type == GeoShape.LON){
            minDoubleValue = lon1
            maxDoubleValue = lon2
            doubleStep = lonStep
            minSingleValue = lat1
            maxSingleValue = lat2
            singleStep = latStep
        }else{
            minDoubleValue = lat1
            maxDoubleValue = lat2
            doubleStep = latStep
            minSingleValue = lon1
            maxSingleValue = lon2
            singleStep = lonStep
        }

        // Create geoPoints
        Set<GeoInterval> geoIntervals = new HashSet<GeoInterval>()
        for(juris in jurisdictions){
            geoIntervals.add(juris.toGeoInterval(type))
        }

        // Create buckets
        List<GeoInterval>[][] tempBuckets = new List<GeoInterval>[dimensions][dimensions]
        for(gi in geoIntervals){
            addToTempBuckets(gi, tempBuckets)
        }
        
        buckets = new GeoInterval[dimensions][dimensions][]
        for(int i = 0; i < dimensions; i++){
            for(int j = 0; j < dimensions; j++){
                if(tempBuckets[i][j] == null){
                    buckets[i][j] = ZERO_LENGTH_ARRAY
                }else{
                    buckets[i][j] = tempBuckets[i][j].toArray(ZERO_LENGTH_ARRAY)
                }
            }
        }
    }

    public void addToTempBuckets(GeoInterval gi, List<GeoInterval>[][] tempBuckets){
        int minDoubleLoc = (gi.retrieveMinDoubleValue() - minDoubleValue)/doubleStep
        int maxDoubleLoc = (gi.retrieveMaxDoubleValue() - minDoubleValue)/doubleStep
        int singleLoc = (gi.retrieveSingleValue() - minSingleValue)/singleStep
        for(int doubleLoc = minDoubleLoc; doubleLoc <= maxDoubleLoc; doubleLoc++){
            if(tempBuckets[doubleLoc][singleLoc] == null){
                tempBuckets[doubleLoc][singleLoc] = new LinkedList<GeoInterval>()
            }
            tempBuckets[doubleLoc][singleLoc].add(gi)
        }
    }

    public Set<Jurisdiction> findIntersectingIntervals(GeoInterval gi){
        Set<Jurisdiction> jurisdictions = new HashSet<Jurisdiction>()
        int minSingleValueLoc = (gi.retrieveMinDoubleValue() - minSingleValue)/singleStep
        int maxSingleValueLoc = (gi.retrieveMaxDoubleValue() - minSingleValue)/singleStep
        int doubleValueLoc = (gi.retrieveSingleValue() - minDoubleValue)/doubleStep
        if(minSingleValueLoc < 0){
            minSingleValueLoc = 0
        }
        if(maxSingleValueLoc >= dimensions){
            maxSingleValueLoc = dimensions - 1
        }
        if(doubleValueLoc < 0){
            doubleValueLoc = 0
        }else if(doubleValueLoc >= dimensions){
            doubleValueLoc = dimensions - 1
        }
        for(int singleValueLoc = minSingleValueLoc; singleValueLoc <= maxSingleValueLoc; singleValueLoc++){
            int length = buckets[doubleValueLoc][singleValueLoc].length
            for(int j = 0; j < length; j++){
                if(gi.intersectsPerpendicularInterval(buckets[doubleValueLoc][singleValueLoc][j])){
                    jurisdictions.add(buckets[doubleValueLoc][singleValueLoc][j].juris)
                }
            }
        }
        return jurisdictions
    }


}

